﻿package com.flashframeless.audio {
	
	/* Original code, by André Anaya - www.andreanaya.com */
	
	import flash.display.Loader;
	import flash.display.Sprite;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent
	import flash.events.ProgressEvent;
	
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundTransform;
	
	import flash.net.URLRequest;
	
	public class SoundControl {
		//Load Control
		private static var _init:Boolean = false;
		private static var _running:Boolean = false;
		private static var loader:Loader;
		
		private static var loaderQueue:Array = new Array();
		private static var _lib:Array = new Array();
		
		private static var _loading:Boolean = false;
		private static var _index:Number = 0;
		
		//Sound Control
		private static var _volume:Number = 1;
		private static var _fade:Number = 1;
		private static var _sprite:Sprite = new Sprite();
		
		//Soundtrack
		private static var _transition:String = "dry";
		
		private static var _nextSoundTrack:String;
		
		private static var playing:Number = 0;
		private static var _soundtrackVolume:Number = 1;
		
		private static var _soundtrack1:Sound;
		private static var _channel1:SoundChannel;
		
		private static var _soundtrack2:Sound;
		private static var _channel2:SoundChannel;
		
		//Continuous Sound
		private static var _continuousLib:Array = new Array();
		
		//IEvent
		private static var dispatcher:EventDispatcher = new EventDispatcher();
		
		public function SoundControl():void{
			trace ("SoundControl is a static class and should not be instantiated.");
		}
		
		//Load Control
		public static function loadSound(url:String, type:String, name:String = ""):void{
			SoundCheck.init();
			
			var chk:Boolean = true;
			var i:Number;
			
			for(i = 0; i<loaderQueue.length; i++){
				if(loaderQueue[i].url == url){
					chk = false;
				}
				if(loaderQueue[i].name == name && name != ""){
					//throw new Error("Name \""+name+"\" already exists.");
				}
			}
			
			for(i = 0; i<_lib.length; i++){
				if(_lib[i].url == url){
					chk = false;
				}
				if(_lib[i].name == name && name != ""){
					//throw new Error("Name \""+name+"\" already exists.");
				}
			}
			
			if(chk){
				if(SoundCheck.soundEnabled){
					if(type == "mp3"){
						var request:URLRequest = new URLRequest(url);
						var sound:Sound = new Sound();
						loaderQueue.push({url:url, type:type, name:name, sound:sound});
					} else if(type == "swf"){
						loaderQueue.push({url:url, type:type, name:name});
					}
					initLoader();
					start();
				} else {
					dispatchEvent(new SoundControlEvent(SoundControlEvent.ITEM_COMPLETE));
					dispatchEvent(new SoundControlEvent(SoundControlEvent.COMPLETE));
				}
			} else {
				if(loaderQueue.length == 0){
					dispatchEvent(new SoundControlEvent(SoundControlEvent.ITEM_COMPLETE));
					dispatchEvent(new SoundControlEvent(SoundControlEvent.COMPLETE));
				}
			}
		}
		private static function start():void{
			if(!_running){
				_running = true;
				_index = 0;
				load();
			}
		}
		private static function load():void{
			if(loaderQueue[_index].type == "mp3"){
				var request:URLRequest = new URLRequest(loaderQueue[_index].url);
				
				var sound:Sound = loaderQueue[_index].sound;
				sound.addEventListener(Event.COMPLETE, completeHandler);
				sound.addEventListener(Event.ID3, id3Handler);
				sound.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
				sound.addEventListener(ProgressEvent.PROGRESS, progressHandler);
				sound.load(request);
			} else if(loaderQueue[_index].type == "swf"){
				loader.load(new URLRequest(loaderQueue[_index].url));
			}
		}
		
		private static function initLoader():void{
			if(!_init){
				_init = true;
				
				loader = new Loader();
				
				configureListeners(loader.contentLoaderInfo);
			}
		}
		
		private static function configureListeners(dispatcher:IEventDispatcher):void {
            dispatcher.addEventListener(Event.COMPLETE, completeHandler);
            dispatcher.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
            dispatcher.addEventListener(Event.INIT, initHandler);
            dispatcher.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
            dispatcher.addEventListener(Event.OPEN, openHandler);
            dispatcher.addEventListener(ProgressEvent.PROGRESS, progressHandler);
            dispatcher.addEventListener(Event.UNLOAD, unLoadHandler);
        }
		
		private static function completeHandler(event:Event):void {
			var soundObj:Object = loaderQueue[_index];
			if(loaderQueue[_index].type == "swf"){
				soundObj.sound = event.currentTarget.content.getSound();
				loader.unload();
			}
			_lib.push(soundObj);
			
			_index++;
			dispatchEvent(new SoundControlEvent(SoundControlEvent.ITEM_COMPLETE));
			
			if(_index == loaderQueue.length){
				_running = false;
				loaderQueue.splice(0, loaderQueue.length);
				dispatchEvent(new SoundControlEvent(SoundControlEvent.COMPLETE));
			}else{
				load();
			}
        }

		private static function httpStatusHandler(event:HTTPStatusEvent):void {
            //trace("httpStatusHandler: " + event);
        }

        private static function initHandler(event:Event):void {
            //trace("initHandler: " + event);
        }

        private static function ioErrorHandler(event:IOErrorEvent):void {
            //trace("ioErrorHandler: " + event);
        }

        private static function openHandler(event:Event):void {
            //trace("openHandler: " + event);
        }

        private static function progressHandler(event:ProgressEvent):void {
			dispatchEvent(new SoundControlEvent(SoundControlEvent.PROGRESS, {bytesLoaded:(_index+1*event.bytesLoaded/event.bytesTotal)/loaderQueue.length, bytesTotal:loaderQueue.length}));
			dispatchEvent(new SoundControlEvent(SoundControlEvent.CURRENT_PROGRESS, {bytesLoaded:event.bytesLoaded, bytesTotal:event.bytesTotal}));
        }

        private static function unLoadHandler(event:Event):void {
            //trace("unLoadHandler: " + event);
        }

		private static function id3Handler(event:Event):void {
            //trace("id3Handler: " + event);
        }
		
		//Lib Control
		public static function getSoundByIndex(index:Number):Sound{
			return _lib[index].sound
		}
		public static function getSoundByName(name:String):Sound{
			var _sound:Sound;
			
			for(var i:Number = 0; i<_lib.length; i++){
				if(_lib[i].name == name){
					_sound = _lib[i].sound;
				}
			}
			
			return _sound;
		}
		public static function getSoundByURL(url:String):Sound{
			var _sound:Sound;
			
			for(var i:Number = 0; i<_lib.length; i++){
				if(_lib[i].url == url){
					_sound = _lib[i].sound;
				}
			}
			
			return _sound;
		}
		private static function getSound(str:String):Sound{
			var _sound:Sound;
			
			for(var i:Number = 0; i<_lib.length; i++){
				if(_lib[i].name == str || _lib[i].url == str){
					_sound = _lib[i].sound;
				}
			}
			
			return _sound;
		}
		
		public static function get lib():Array{
			var arrLib:Array = new Array();
			for(var i:Number = 0; i<_lib.length; i++){
				arrLib.push({index:i, name:_lib[i].name, url:_lib[i].url});
			}
			return arrLib;
		}
		
		public static function get queueIndex():Number{
			return _index;
		}
		
		public static function get channel():SoundChannel{
			return _channel1;
		}		
		//Play Control
		
		//Volume
		public static function set volume(n:Number):void{
			if(n>1){
				n = 1;
			} else if(n<0){
				n = 0;
			}
			
			updateVolume(n);
			
			_volume = n;
			_fade = n;
		}
		public static function get volume():Number{
			return _volume;
		}
		
		//Fade
		public static function fadeIn():void{
			_sprite.addEventListener(Event.ENTER_FRAME, setFadeIn);
			_sprite.removeEventListener(Event.ENTER_FRAME, setFadeOut);
		}
		public static function setFadeIn(e:Event):void{
			if(_fade < _volume){
				_fade += .05;
			} else {
				_fade = _volume;
				_sprite.removeEventListener(Event.ENTER_FRAME, setFadeIn);
			}
			updateVolume(_fade);
		}
		
		public static function fadeOut():void{
			_sprite.addEventListener(Event.ENTER_FRAME, setFadeOut);
			_sprite.removeEventListener(Event.ENTER_FRAME, setFadeIn);
		}
		public static function setFadeOut(e:Event):void{
			if(_fade > 0){
				_fade -= .05;
			} else {
				_fade = 0;
				_sprite.removeEventListener(Event.ENTER_FRAME, setFadeOut);
			}
			updateVolume(_fade);
		}
		
		private static function updateVolume(n:Number):void{
			if(SoundCheck.soundEnabled){
				var channel:SoundChannel;
				var transform:SoundTransform;
				
				for(var i:Number = 0; i<_continuousLib.length; i++){
					channel = _continuousLib[i].channel;
					transform = _continuousLib[i].transform;
					
					transform.volume = transform.volume*n;
					channel.soundTransform = transform;
				}
				
				if(_channel1){
					transform = _channel1.soundTransform;
					transform.volume = n*_soundtrackVolume;
					_channel1.soundTransform = transform;
				}
				
				if(_channel2){
					transform = _channel2.soundTransform;
					transform.volume = n*_soundtrackVolume;
					_channel2.soundTransform = transform;
				}
			}
		}
		
		//Soundtrack
		public static function set transition(type:String):void{
			if(type == "dry"){
				_transition = "dry";
			}else if(type == "fadeIn"){
				_transition = "fadeIn";
			}else if(type == "fadeOut"){
				_transition = "fadeOut";
			}else if(type == "fade"){
				_transition = "fade";
			}else if(type == "crossFade"){
				_transition = "crossFade";
			}
		}
		public static function get transition():String{
			return _transition;
		}
		public static function soundTrack(sound:*):void{
			if(SoundCheck.soundEnabled){
				var _sound:Sound;
				
				if(isNaN(sound)){
					_sound = getSound(sound);
				}else{
					_sound = getSoundByIndex(sound);
				}
				
				var _transform:SoundTransform;
				
				if(_sprite.hasEventListener(Event.ENTER_FRAME)){
					_sprite.removeEventListener(Event.ENTER_FRAME, changeSoundtrack);
				}
				if(playing == 0){
					playing = 1;
					_soundtrack1 = _sound;
					_channel1 = _soundtrack1.play(0, 99999999999999999999);
					
					_transform = _channel1.soundTransform;
					_transform.volume = _fade*_soundtrackVolume;
					_channel1.soundTransform = _transform;
					_channel1.addEventListener(Event.SOUND_COMPLETE, loop);
				} else if(playing == 1){
					if(_channel2){
						_channel2.stop();
					}
					
					playing = 2;
					_soundtrack2 = _sound;
					_channel2 = _soundtrack2.play(0, 99999999999999999999);
					
					_transform = _channel2.soundTransform;
					_transform.volume = 0;
					_channel2.soundTransform = _transform;
					_channel2.addEventListener(Event.SOUND_COMPLETE, loop);
					
					_sprite.addEventListener(Event.ENTER_FRAME, changeSoundtrack);
				} else if(playing == 2){
					if(_channel1){
						_channel1.stop();
					}
					
					playing = 1;
					_soundtrack1 = _sound;
					_channel1 = _soundtrack1.play(0, 99999999999999999999);
					
					_transform = _channel1.soundTransform;
					_transform.volume = 0;
					_channel1.soundTransform = _transform;
					_channel1.addEventListener(Event.SOUND_COMPLETE, loop);
					
					_sprite.addEventListener(Event.ENTER_FRAME, changeSoundtrack);
				}
			}
		}
		private static function loop(e:Event):void{
			var _transform:SoundTransform;
			
			if(playing == 1){
				_transform = _channel1.soundTransform;
				_channel1 = _soundtrack1.play(0, 99999999999999999999);
				_channel1.soundTransform = _transform;
				
				_channel1.addEventListener(Event.SOUND_COMPLETE, loop);
			} else if(playing == 2){
				_transform = _channel2.soundTransform;
				_channel2 = _soundtrack2.play(0, 99999999999999999999);
				_channel2.soundTransform = _transform;
				
				_channel2.addEventListener(Event.SOUND_COMPLETE, loop);
			}
		}
		
		private static function changeSoundtrack(e:Event):void{
			var _transform:SoundTransform;
			
			if(_transition == "dry"){
				if(playing == 1){
					_transform = _channel1.soundTransform;
					_transform.volume = 1*_fade*_soundtrackVolume;
					_channel1.soundTransform = _transform;
					
					
					_transform = _channel2.soundTransform;
					_transform.volume = 0;
					_channel2.soundTransform = _transform;
					
					_channel2.stop();
				}else if(playing == 2){
					_transform = _channel1.soundTransform;
					_transform.volume = 0;
					_channel1.soundTransform = _transform;
					
					
					_transform = _channel2.soundTransform;
					_transform.volume = 1*_fade*_soundtrackVolume;
					_channel2.soundTransform = _transform;
					
					_channel1.stop();
				}
				_sprite.removeEventListener(Event.ENTER_FRAME, changeSoundtrack);
			} else if(_transition == "fadeIn"){
				if(playing == 1){
					if(_channel2.soundTransform.volume == 0){
						_transform = _channel1.soundTransform;
						
						if(_transform.volume < 1*_fade*_soundtrackVolume){
							_transform.volume += .1;
							_channel1.soundTransform = _transform;
						}else{
							_transform.volume = 1*_fade*_soundtrackVolume;
							_channel1.soundTransform = _transform;
							
							_channel2.stop();
							_sprite.removeEventListener(Event.ENTER_FRAME, changeSoundtrack);
						}
					
					}else{
						_transform = _channel2.soundTransform;
						_transform.volume = 0;
						_channel2.soundTransform = _transform;
					}
				}else if(playing == 2){
					if(_channel1.soundTransform.volume == 0){
						_transform = _channel2.soundTransform;
						
						if(_transform.volume < 1*_fade*_soundtrackVolume){
							_transform.volume += .1;
							_channel2.soundTransform = _transform;
						}else{
							_transform.volume = 1*_fade*_soundtrackVolume;
							_channel2.soundTransform = _transform;
							
							_channel1.stop();
							_sprite.removeEventListener(Event.ENTER_FRAME, changeSoundtrack);
						}
					
					}else{
						_transform = _channel1.soundTransform;
						_transform.volume = 0;
						_channel1.soundTransform = _transform;
					}
				}
			} else if(_transition == "fadeOut"){
				if(playing == 1){
					if(_channel2.soundTransform.volume > 0){
						_transform = _channel2.soundTransform;
						_transform.volume -= .1;
						_channel2.soundTransform = _transform;
					}else{
						_transform = _channel1.soundTransform;
						_transform.volume = 1*_fade*_soundtrackVolume;
						_channel1.soundTransform = _transform;
						
						_channel2.stop();
						_sprite.removeEventListener(Event.ENTER_FRAME, changeSoundtrack);
					}
				}else if(playing == 2){
					if(_channel1.soundTransform.volume > 0){
						_transform = _channel1.soundTransform;
						_transform.volume -= .1;
						_channel1.soundTransform = _transform;
					}else{
						_transform = _channel2.soundTransform;
						_transform.volume = 1*_fade*_soundtrackVolume;
						_channel2.soundTransform = _transform;
						
						_channel1.stop();
						_sprite.removeEventListener(Event.ENTER_FRAME, changeSoundtrack);
					}
				}
			} else if(_transition == "fade"){
				if(playing == 1){
					if(_channel2.soundTransform.volume > 0){
						_transform = _channel2.soundTransform;
						_transform.volume -= .1;
						_channel2.soundTransform = _transform;
					}else{
						_transform = _channel1.soundTransform;
						if(_transform.volume < 1*_fade*_soundtrackVolume){
							_transform.volume += .1;
							_channel1.soundTransform = _transform;
						}else{
							_transform.volume = 1*_fade*_soundtrackVolume;
							_channel1.soundTransform = _transform;
							
							_channel2.stop();
							_sprite.removeEventListener(Event.ENTER_FRAME, changeSoundtrack);
						}
					}
				} else if(playing == 2){
					if(_channel1.soundTransform.volume > 0){
						_transform = _channel1.soundTransform;
						_transform.volume -= .1;
						_channel1.soundTransform = _transform;
					}else{
						_transform = _channel2.soundTransform;
						if(_transform.volume < 1*_fade*_soundtrackVolume){
							_transform.volume += .1;
							_channel2.soundTransform = _transform;
						}else{
							_transform.volume = 1*_fade*_soundtrackVolume;
							_channel2.soundTransform = _transform;
							
							_channel1.stop();
							_sprite.removeEventListener(Event.ENTER_FRAME, changeSoundtrack);
						}
					}
				}
			} else if(_transition == "crossFade"){
				if(playing == 1){
					if(_channel2.soundTransform.volume > 0){
						_transform = _channel2.soundTransform;
						_transform.volume -= .05;
						_channel2.soundTransform = _transform;
						
						_transform = _channel1.soundTransform;
						_transform.volume = 1*_fade*_soundtrackVolume-_channel2.soundTransform.volume;
						_channel1.soundTransform = _transform;
					}else{
						_channel2.stop();
						_sprite.removeEventListener(Event.ENTER_FRAME, changeSoundtrack);
					}
				} else if(playing == 2){
					if(_channel1.soundTransform.volume > 0){
						_transform = _channel1.soundTransform;
						_transform.volume -= .05;
						_channel1.soundTransform = _transform;
						
						_transform = _channel2.soundTransform;
						_transform.volume = 1*_fade*_soundtrackVolume-_channel1.soundTransform.volume;
						_channel2.soundTransform = _transform;
					}else{
						_channel1.stop();
						_sprite.removeEventListener(Event.ENTER_FRAME, changeSoundtrack);
					}
				}
			}
		}
		public static function set soundtrackVolume(n:Number):void{
			if(n>1){
				n = 1;
			} else if(n<0){
				n = 0;
			}
			
			_soundtrackVolume = n;
			
			var transform:SoundTransform;
			
			if(_channel1){
				transform = _channel1.soundTransform;
				transform.volume = _soundtrackVolume;
				_channel1.soundTransform = transform;
			}
			
			if(_channel2){
				transform = _channel2.soundTransform;
				transform.volume = _soundtrackVolume;
				_channel2.soundTransform = transform;
			}
		}
		public static function get soundtrackVolume():Number{
			return _soundtrackVolume;
		}
		
		//Event Sound;
		public static function eventSound(sound:*, volume:Number = 1, pan:Number = 0):void{
			if(SoundCheck.soundEnabled){
				var _sound:Sound;
				
				if(isNaN(sound)){
					_sound = getSound(sound);
				}else{
					_sound = getSoundByIndex(sound);
				}
				
				var _channel:SoundChannel = _sound.play();
				
				var _transform:SoundTransform = _channel.soundTransform;
				_transform.volume = volume*_fade;
				_transform.pan = pan;
				_channel.soundTransform = _transform;
			}
		}
		
		//Continuous Sound
		public static function continuousSound(sound:*, name:String):void{
			var chk:Boolean = true;
			for(var i:Number = 0; i<_continuousLib.length; i++){
				if(_continuousLib[i].name == name){
					chk = false;
				}
			}
			
			if(chk){
				var _sound:Sound;
				
				if(SoundCheck.soundEnabled){
					if(isNaN(sound)){
						_sound = getSound(sound);
					}else{
						_sound = getSoundByIndex(sound);
					}
					
					var _channel:SoundChannel = _sound.play(0, 99999999999999999999);
					_channel.addEventListener(Event.SOUND_COMPLETE, playContinuous);
					
					var _transform:SoundTransform = _channel.soundTransform;
					_transform.volume = _fade;
					_channel.soundTransform = _transform;
				}
				
				_continuousLib.push({sound:_sound, channel:_channel, transform:_transform, name:name});
			} else {
				//throw new Error("Name \""+name+"\" already exists.");
			}
		}
		public static function transformSound(name:String, volume:Number = 1, pan:Number = 0):void{
			if(SoundCheck.soundEnabled){
				var channel:SoundChannel;
				var transform:SoundTransform;
				
				for(var i:Number = 0; i<_continuousLib.length; i++){
					if(_continuousLib[i].name == name){
						channel = _continuousLib[i].channel;
						transform = _continuousLib[i].transform;
						
						transform.volume = volume*_fade;
						transform.pan = pan;
						channel.soundTransform = transform;
					}
				}
			}
		}
		public static function stopSound(name:String):void{
			for(var i:Number = 0; i<_continuousLib.length; i++){
				if(_continuousLib[i].name == name){
					if(SoundCheck.soundEnabled){
						_continuousLib[i].channel.stop();
					}
					_continuousLib.splice(i, 1);
					
					break;
				}
			}
		}
		private static function playContinuous(e:Event):void{
			for(var i:Number = 0; i<_continuousLib.length; i++){
				if(_continuousLib[i].channel == e.currentTarget){
					if(SoundCheck.soundEnabled){
						_continuousLib[i].channel = _continuousLib[i].sound.play(0, 99999999999999999999);
						_continuousLib[i].channel.soundTransform = _continuousLib[i].transform;
						_continuousLib[i].channel.addEventListener(Event.SOUND_COMPLETE, playContinuous);
					}
					break;
				}
			}
		}
		
		//IEvent
		public static function addEventListener(type : String, listener : Function, useCapture : Boolean = false, priority : int = 0, useWeakReference : Boolean = true) : void {
			dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
		}

		public static function dispatchEvent(evt : Event) : Boolean {
			return dispatcher.dispatchEvent(evt);
		}

		public static function hasEventListener(type : String) : Boolean {
			return dispatcher.hasEventListener(type);
		}

		public static function removeEventListener(type : String, listener : Function, useCapture : Boolean = false) : void {
			dispatcher.removeEventListener(type, listener, useCapture);
		}

		public static function willTrigger(type : String) : Boolean {
			return dispatcher.willTrigger(type);
		}
	}
}